var game;
var avatar;
var disJointDef;
var disJoint = null;
var isMouseDown = false;
var anchor = null;
var newClick = true;
var mouseX, mouseY;
var canvasPosition = getElementPosition(document.getElementById("canvas"));
var gameResource;
// for quick reference
var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,  b2AABB = Box2D.Collision.b2AABB
            ,  b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,  b2Body = Box2D.Dynamics.b2Body
            ,  b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,  b2Fixture = Box2D.Dynamics.b2Fixture
            ,  b2World = Box2D.Dynamics.b2World
            ,  b2MassData = Box2D.Collision.Shapes.b2MassData
            ,  b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,  b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,  b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,  b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;



var world = new b2World( new b2Vec2(0, 10), true);
function init()
{
   game = new Object();
   game.gameSet = false;
   //load resource
   gameResource = new Object();

   // Get the canvas 2d context

   gameResource.canvas = document.getElementById('canvas').getContext('2d');

   // Create a new image.
   gameResource.goalImg = new Image();

   // Once it's loaded draw the image on the canvas.
   gameResource.goalImg.src = 'star.png';



   // for keyboard event
   document.onkeypress = keyPressHandler;
   
  


   var fixDef = new b2FixtureDef;
   fixDef.density = 1.0;
   fixDef.friction = 0.5;
   fixDef.restitution = 0.2;

   var bodyDef = new b2BodyDef;
         
   //create ground
   bodyDef.type = b2Body.b2_staticBody;
   fixDef.shape = new b2PolygonShape;
   fixDef.shape.SetAsBox(640 / 30, 2);
   bodyDef.position.Set(10, 360 / 30 + 1.8);
   world.CreateBody(bodyDef).CreateFixture(fixDef);
   bodyDef.position.Set(10, -1.8);
   world.CreateBody(bodyDef).CreateFixture(fixDef);
   fixDef.shape.SetAsBox(2, 360 / 30);
   bodyDef.position.Set(-1.8, 12);
   world.CreateBody(bodyDef).CreateFixture(fixDef);
   bodyDef.position.Set(640 / 30 + 1.8, 12);
   world.CreateBody(bodyDef).CreateFixture(fixDef);

   //create the avatar
   bodyDef.type = b2Body.b2_dynamicBody;
   fixDef.density = 1.0;
   fixDef.friction = 0.5;
   fixDef.restitution = 0.2;
   fixDef.shape = new b2CircleShape(0.15);
   bodyDef.position.Set(10, 1);
   avatar = world.CreateBody(bodyDef);
   avatar.CreateFixture(fixDef);

   //create the anchor
   bodyDef.type = b2Body.b2_staticBody;
   fixDef.density = 0.0;
   fixDef.friction = 0.5;
   fixDef.restitution = 0.2;
   fixDef.shape = new b2CircleShape(0.1);
   bodyDef.position.Set(10, 1);
   anchor = world.CreateBody(bodyDef);
   anchor.CreateFixture(fixDef);

   loadLevel();


   var debugDraw = new b2DebugDraw();
   debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
   debugDraw.SetDrawScale(30.0);
   debugDraw.SetFillAlpha(0.5);
   debugDraw.SetLineThickness(1.0);
   debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
   world.SetDebugDraw(debugDraw);


}

function createAnchor(ax, ay)
{
   anchor.SetPosition(new b2Vec2(ax, ay));
}

function setDisJoint(bA, bB)
{
   disJointDef = new Box2D.Dynamics.Joints.b2RevoluteJointDef();
   disJointDef.Initialize(bA, bB, bA.GetWorldCenter(), bB.GetWorldCenter());
   /*disJointDef.bodyA = bA;
   disJointDef.bodyB = bB;
   disJointDef.localAnchorA.Set(0, 0);
   disJointDef.localAnchorB.Set(0, 0);
   */
   var tmp1, tmp2;

   tmp1 = (bA.GetPosition().x  - bB.GetPosition().x);
   tmp1 = tmp1* tmp1;
   tmp2 = (bA.GetPosition().x  - bB.GetPosition().y);
   tmp2 = tmp2 * tmp2;
   disJointDef.length = Math.sqrt(tmp1 + tmp2);

   //disJointDef.enableLimit = true;
   disJointDef.dampingRatio = 1.0;
   disJointDef.frequencyHZ = 0.0;
   disJointDef.collideConnected = true;

   disJoint = world.CreateJoint(disJointDef);

   //DestroyJoint(j:b2Joint):void
   //DestroyBody(b:b2Body):void
}

document.addEventListener("mousedown", function(e) 
{
   isMouseDown = true;
   handleMouseMove(e);
}, true);

document.addEventListener("mouseup", function() 
{
   newClick = true;
   world.DestroyJoint(disJoint);
   disJoint = null;
   isMouseDown = false;

   mouseX = undefined;
   mouseY = undefined;
   
}, true);

function handleMouseMove(e) 
{
   mouseX = (e.clientX - canvasPosition.x) / 30;
   mouseY = (e.clientY - canvasPosition.y) / 30;
};

//helper
function getElementPosition(element) {

   var elem=element, tagname="", x=0, y=0;
            while((typeof(elem) == "object") && (typeof(elem.tagName) != "undefined")) {
               y += elem.offsetTop;
               x += elem.offsetLeft;
               tagname = elem.tagName.toUpperCase();
               if(tagname == "BODY")
                  elem=0;
               if(typeof(elem) == "object") {
                  if(typeof(elem.offsetParent) == "object")
                     elem = elem.offsetParent;
               }
            }
            return {x: x, y: y};
         }

function update() {

   if (isMouseDown && newClick)// newly clicked
   {
      newClick = false;
      createAnchor(mouseX, mouseY);
      setDisJoint(anchor, avatar);
   }

   if (gameCheck())
   {
      game.gameSet = true;
   }
   else
   {
      //game.gameSet = false;
   }
   requestAnimFrame(update);
   world.Step(1 / 60, 10, 10);
   world.DrawDebugData();
   world.ClearForces();
   gameRender();

};

// this function checks if the player wins
function gameCheck()
{
   if (avatar == undefined)
      return false;
   var ty = avatar.GetPosition().y - game.level.goaly;
   var tx = avatar.GetPosition().x - game.level.goalx;
   var res = Math.sqrt(tx * tx + ty * ty);
   if (res < 0.4)
      return true;
   return false;
}

// this function handles the drawing job of the game
function gameRender()
{
   if (gameResource != undefined)
      gameResource.canvas.drawImage(gameResource.goalImg, game.level.goalx * 30 - 12, game.level.goaly * 30 - 12);

   if (game != undefined)
   {
      if(game.gameSet)
      {
         gameResource.canvas.fillStyle = '#f00';
         gameResource.canvas.font = 'bold 18px helvetica';
         gameResource.canvas.fillText('You win!', 300, 180, 200);
      }
   }
}

window.requestAnimFrame = (function(){
   return   window.requestAnimationFrame       || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function(callback, element){
            window.setTimeout(callback, 1000 / 60);
   };
})();


function keyPressHandler(e){
   var evtobj=window.event? event : e //distinguish between IE's explicit event object (window.event) and Firefox's implicit.
   var unicode=evtobj.charCode? evtobj.charCode : evtobj.keyCode
   var actualkey=String.fromCharCode(unicode)

   if (actualkey == "d")
   {
      avatar.ApplyTorque(-0.5);
      //avatar.ApplyForce(new b2Vec2(avatar.GetPosition().x,
        // avatar.GetPosition().y), avatar.GetWorldCenter());
   }
   if (actualkey == "a")
   {
      avatar.ApplyTorque(0.5);
   }

   if (actualkey == 'r')
   {
      resetLevel();
   }
}

function resetLevel()
{
   game.gameSet = false;
   avatar.SetPosition(new b2Vec2(game.level.avatarx, game.level.avatary));
   
}

function loadLevel()
{
   game.level = {
     "avatarx": 3,
     "avatary" : 1,
     "goalx": 20,
     "goaly"  : 11,
     "ground":
     [
        {
            "shape":
            [
                0.5,
                3, 
                3,
                3,
                3,
                3.5,
                0.5,
                3.5
            ]
        },
        {
            "shape":
            [
                19.3,
                7.5, 
                21.3,
                7.5,
                21.3,
                10,
                19.3,
                10
            ]
        },
        {
            "shape":
            [
                0,
                12, 
                7,
                9,
                7,
                12
            ]
        }
     ]
   };

   // set avatar x, and y
   avatar.SetPosition(new b2Vec2(game.level.avatarx, game.level.avatary));
   
   var fixDef = new b2FixtureDef;
   fixDef.density = 1.0;
   fixDef.friction = 0.5;
   fixDef.restitution = 0.2;

   var bodyDef = new b2BodyDef;
         
   //create ground
   bodyDef.type = b2Body.b2_staticBody;
   fixDef.shape = new b2PolygonShape;
   
   gameResource.levelBody = [];
   // set gound
   for (var i = 0; i < game.level.ground.length; ++i)
   {
      var shape = game.level.ground[i].shape;
      var points = [];
      //crate a new shape
      for (var j = 0; j < shape.length /2; ++j)
      {
         var vec = new b2Vec2();
         vec.Set(shape[j * 2], shape[j * 2 + 1]);
         points[j] = vec;
      }
      fixDef.shape.SetAsArray(points, points.length);
      world.CreateBody(bodyDef).CreateFixture(fixDef);
   }
   
}

update();
